﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Awesom_O_Controller
{
    public abstract class AoSocket : IDisposable
    {
        private readonly string _ip = "127.0.0.1";
        private readonly int _port = 2004;
        private readonly Socket _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        private Thread _receiverthread;

        protected AoSocket() { }

        protected AoSocket(string ip)
        {
            _ip = ip;
        }

        protected AoSocket(string ip, int port)
        {
            _ip = ip;
            _port = port;
        }

        public bool IsClientConnected
        {
            get
            {
                try { return _client.Connected; }
                catch (Exception e)
                {
                    SocketErrorHandler(e.Message);
                    return false;
                }
            }
        }

        protected bool Connect()
        {
            try
            {
                _client.Connect(new IPEndPoint(IPAddress.Parse(_ip), _port));
                return _client.Connected;
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return false;
        }

        protected bool StartListener()
        {
            try
            {
                if (_client.Connected)
                {
                    _receiverthread = new Thread(ListenerWorker);
                    _receiverthread.Start();
                    return true;
                }
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return false;
        }

        protected bool Disconnect()
        {
            try
            {
                _client.Disconnect(true);
                return _client.Connected;
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return false;
        }

        protected int Send(byte[] buffer, int length)
        {
            try
            {
                if (_client.Connected)
                    return _client.Send(buffer, length, SocketFlags.None);
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return 0;
        }

        protected void ListenerWorker(object obj)
        {
            try
            {
                while (_client.Connected)
                {
                    using (var stream = new NetworkStream(_client))
                    {
                        if (stream.DataAvailable)
                        {
                            using (var r = new BinaryReader(stream))
                            {
                                int length = r.ReadInt32();
                                if (length == 0) { break; }
                                byte[] buffer = r.ReadBytes(length);
                                PacketHandler(buffer);
                            }
                        }
                    }
                    Thread.Sleep(100);
                }
                SocketErrorHandler("Core disconnected");
            }
            catch (ThreadAbortException) { }
            catch (Exception e) { SocketErrorHandler(e.Message); }
        }

        abstract protected void PacketHandler(byte[] packets);

        abstract protected void SocketErrorHandler(string errmsg);

        public void Dispose()
        {
            try
            {
                _client.Close();
                if (_receiverthread != null)
                {
                    if (_receiverthread.IsAlive)
                    {
                        _receiverthread.Abort();
                        _receiverthread.Join();
                    }
                }
            }
            catch { }
        }
    }
}
